// Copyright (c) 2009, Philip Tucker
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY Philip Tucker 'AS IS' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Philip Tucker BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package edu.luc.android;

import java.util.ArrayList;
import java.util.List;

import android.location.Address;
import android.os.Handler;
import android.os.Message;
import android.util.Log;


/**
 * Iterates over all searches in supplied MapItems until they're 
 * complete.
 */
public abstract class MapSearcher {
  private static final String LOG_ID =
      MapSearcher.class.getSimpleName();

  /**
   * Are locations bounded?
   */
  private static boolean BOUNDED = true;
  
  private class SearchListener extends Handler {
    private final MapSearch search;

    public SearchListener(MapSearch search) {
      this.search = search;
    }

    @Override
    public void handleMessage(Message msg) {
      Log.v(LOG_ID, "handleMessage");
      List<SearchResultMapItem> locations =
          new ArrayList<SearchResultMapItem>();
      try {
        locations = search.addResults(toAddresses(msg));
        Log.v(LOG_ID, "using results: " + locations.size());
      } catch (Exception ex) {
        Log.e(LOG_ID, "error converting addresses to location", ex);
      } finally {
        // Report results and continue searching.
        MapSearcher.this.onResult(locations);
        MapSearcher.this.continueSearch();
      }
    }

    @SuppressWarnings("unchecked")
    private Iterable<Address> toAddresses(Message msg) {
      List<Address> addresses = (List<Address>) msg.obj;
      Log.v(LOG_ID, "search results: " + addresses.size());
      return addresses;
    }
  };

  private final MapItems locations;

  private final LocalSearcher searcher;

  public MapSearcher(LocalSearcher searcher,
      MapItems locations) {
    this.locations = locations;
    this.searcher = searcher;
  }

  public synchronized void startSearch() {
    Log.v(LOG_ID, "startSearch");
    locations.resetSearch();
    continueSearch();
  }

  private synchronized void continueSearch() {
    Log.v(LOG_ID, "continueSearch");
    for (MapSearch search : locations.getSearches()) {
      if (!search.isComplete()) {
        String query = search.nextQuery();
        SearchListener listener = new SearchListener(search);
        try {
          searcher.search(query, BOUNDED, listener);
        } catch (Exception ex) {
          Log.v(LOG_ID, "failed search: " + query, ex);
          continueSearch();
        }
        return;
      }
    }

    // If we make here, no searches left to perform.
    onSearchComplete();
  }

  protected abstract void onResult(Iterable<SearchResultMapItem> locations);

  protected abstract void onSearchComplete();
}
